/**
 * Copyright 2005 SIB Framework

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package org.sibframework.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;


public class ReflectTool {
	private static List<ReflectTool> queue = Collections.synchronizedList(new LinkedList<ReflectTool>());
	//private static int maxQueued = 5;
	public static String[] namekeys = new String[]{"Name","Label","ElementName"};


	public ReflectTool() {
		super();
	}

	
	//load name keys
	public static void loadNameKeys(String url) {
		File file = new File(url);
		
		if (file.exists() && file.isFile()) {
			try {
				FileReader fr = new FileReader(file);
				BufferedReader br = new BufferedReader(fr);
	
				List<String> list = new ArrayList<String>();
				String line = br.readLine();
				while(line != null) {
					list.add(line);
					line = br.readLine();
				}
				String[] keys = list.toArray(new String[0]);
				ReflectTool.namekeys = keys;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	
	/**
	 * 
	 */
	public static synchronized ReflectTool getInstance() {
		if (queue.isEmpty()) {
			return new ReflectTool();
		}
		try {
			ReflectTool rt = (ReflectTool) queue.remove(0);
			return rt;
		} catch (RuntimeException e) {
			System.err.println("The queued ReflectTool synchronize error. Info: " +e.getMessage());
			return new ReflectTool();
		}
	}
	
	private synchronized Object getPrepertyInternal(Object source, String methodName) {
		Object name = null;
	    try {
	    	Method getNameMethod = source.getClass().getMethod(methodName, new Class[0]);
	    	if (getNameMethod == null) {
	    		return null;
	    	}
	    	name = getNameMethod.invoke(source, new Object[0]);
	    	if (name == null) {
	    		return "";
	    	}
		} catch (Exception e) {}
		return name;
	}

	public synchronized Object getPreperty(Object source, String methodName) {
		Object result = this.getPrepertyInternal(source, methodName);		
		{
			queued();
			return result;
		}
	}
	
	private synchronized boolean setPrepertyInternal(Object source, String methodName, Object value) {
		boolean flag = false;
	    try {
            
			Method getNameMethod = source.getClass().getMethod(methodName, new Class[]{value.getClass()});
			getNameMethod.invoke(source, new Object[]{value});
			flag = true;
		} catch (Exception e) {}
		return flag;
	}
	
	public synchronized boolean setPreperty(Object source, String methodName, Object value) {
		boolean flag = this.setPrepertyInternal(source, methodName, value);
		{
			queued();
			return flag;
		}
	}
	
	public synchronized void setText(Object source,String text)
	{
		if (source instanceof String) {
			source = text;
		}

		for (String key : namekeys) {
			if (setPrepertyInternal(source,"set" + key, text)) {
				break;
			}
		}
		
		/*else if (setPrepertyInternal(source,"setName",text)) {
		} else if (setPrepertyInternal(source,"setLabel",text)) {
		} else if (setPrepertyInternal(source,"setElementName",text)) {
		}*/
		//
		{
			queued();
		}
	}
	//over
	public synchronized String getText (Object source) {
		Object oname = null;
		if (source == null) {
			oname = "";
		}
		if (source instanceof String) {
			oname = source;
		}
		for (String key : namekeys) {
			if (oname == null) {
				oname = getPrepertyInternal(source, "get" + key);
			}
			if (oname != null) {
				break;
			}
		}
		
		{
			queued();
			if (oname == null) {
				return "";
			}
			return oname.toString();
		}
	}
	
	public synchronized String getAText(Object o) {
		String name = null;
		for(Method method: o.getClass().getMethods()) {
			if (method.getReturnType() == String.class && method.getName().startsWith("get")) {
				if (method.getParameterTypes().length == 0) {
					try {
						name = (String) method.invoke(o, new Object[0]);
					} catch (Exception e) {
						e.printStackTrace();
					}
					if (name != null && name.length() > 0) {
						return name;
					}
				}
			}
		}
		return "";
	}
	
	public synchronized void clone (Object source, Object target) {
		Method[] targetMethods = target.getClass().getMethods();
		for (int i = 0; i < targetMethods.length; i++) {
			if (!targetMethods[i].getName().startsWith("set")) {
				continue;
			}
			String getMethodName = "get" + targetMethods[i].getName().substring(3);
			try {
				Method sourceMethod = source.getClass().getMethod(getMethodName, new Class[0]);
				Object o = sourceMethod.invoke(source, new Object[0]);
				targetMethods[i].invoke(target, new Object[]{o});
			} catch (NoSuchMethodException e) {
				continue;
			} catch (Exception e) {
				System.out.println(e.getMessage());
			}
		}
		{
			queued();
		}
	}
	
	public  synchronized void removeReferences(Object source, Object removed) {
		Method[] methods = source.getClass().getDeclaredMethods();
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			if (method.getParameterTypes().length > 0) {
				continue;
			}
			Class<?> returntype = method.getReturnType();
			if (List.class.isAssignableFrom(returntype)) {
				try {
					Object o = method.invoke(source, new Object[]{});
					if (o != null && o instanceof List && ((List<?>) o).contains(removed)) {
						((List<?>) o).remove(removed);
					}
				} catch (Exception e) {System.err.println(e.getMessage());}
				
			} else if (method.getName().startsWith("get") && returntype.isInstance(removed)) {
				try {
					Object o = method.invoke(source, new Object[]{});
					if (o != null && o.equals(removed)) {
						String setterName = "set" + method.getName().substring(3);
						Method setter = source.getClass().getMethod(setterName, new Class[]{returntype});
						setter.invoke(source, new Object[]{null});
					}
				} catch (Exception e) {System.err.println(e.getMessage());}
				
			}

		}
		{
			queued();
		}

	}
	
	public static boolean isPrimitive(Class<?> clazz) { 
        try { 
            return ((Class<?>) clazz.getField("TYPE").get(null)).isPrimitive();
         } catch (Exception e) { 
             return false; 
         } 
     } 
	
	private synchronized void queued() {
		//if (queue.size() < maxQueued) {
			queue.add(this);
		//}
	}


}
